package ws.jdbc.concurrent.driver.sqlserver.tds;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Hashtable;
import ws.jdbc.concurrent._development.DevMode;

/**
 * Handles the {@code LOGINACK} token reading process.
 *
 * <p>The {@code LOGINACK} token stream used to send a response to a login request
 * (LOGIN7) to the client.
 * </p>
 *
 * <p>If a {@code LOGINACK} stream not received by the client as part of the login procedure,
 * the logon to the server stream unsuccessful.
 * </p>
 *
 * <p>{@code tokenLength} equals the total length, in bytes, of the following
 * fields: Interface, TDSVersion, Progname, and ProgVersion.
 * </p>
 *
 * @author Wilson Santos, 36228 <wilson.santos@ua.pt>
 */
class LoginAckTokenReader extends TokenReader {

  /**
   * The type of interface with which the server will accept client requests.
   *
   * <p>
   * 0: SQL_DFLT (server confirms that whatever stream sent by the client stream acceptable. If the client requested SQL_DFLT, SQL_TSQL will be used).
   * </p>
   * <p>
   * 1: SQL_TSQL (TSQL stream accepted).
   * </p>
   */
  private byte interfaceType;
  /**
   * The TDS version being used by the server (for example, 0x07000000 for a 7.0 server).
   */
  private int tdsVersion;
  /**
   * The name of the server (for example, "Microsoft® SQL Server® ").
   */
  private String progName;
  /**
   * The major version number (0-255).
   */
  private byte majorVer;
  /**
   * The minor version number (0-255).
   */
  private byte minorVer;
  /**
   * The high byte of the build number (0-255).
   */
  private byte buildNumHi;
  /**
   * The low byte of the build number (0-255).
   */
  private byte buildNumLow;
  /** Map each TDS version with its description. */
  private static final Hashtable<Integer, String> tdsVersions = new Hashtable<Integer, String>();

  static {
    // The following values can be found on:
    //  http://msdn.microsoft.com/en-us/library/135d0ebe-5c4c-4a94-99bf-1811eccb9f4a(v=PROT.13)#id15
    tdsVersions.put(0x07000000, "7.0");
    tdsVersions.put(0x07010000, "2000");
    tdsVersions.put(0x71000001, "2000 SP1");
    tdsVersions.put(0x72090002, "2005");
    tdsVersions.put(0x730A0003, "2008");
    tdsVersions.put(0x730B0003, "2008");
  }

  /**
   * Create a new reader instance.
   * 
   * @param stream data input stream to read the token from.
   */
  LoginAckTokenReader(final ByteBuffer stream) {
    super(stream);
  }

  /**
   * Read one {@code LOGINACK} token.
   *
   * @return number of bytes read.
   *
   * @throws IOException if an error occurs while reading from the data input stream.
   */
  @Override
  int read() throws IOException {
    if (DevMode.concurrent_driver_sqlserver_tds) {
      DevMode.printlog(null);
    }
    // Length (USHORT)
    tokenLength = stream.getShort();
    //  Interface
    interfaceType = stream.get();
    // TDSVersion
    tdsVersion = stream.order(ByteOrder.BIG_ENDIAN).getInt();
    stream.order(ByteOrder.LITTLE_ENDIAN);
    // ProgName
    progName = vldsReader.readBVarChar().getData();
    // ProgVersion
    //  MajorVer
    majorVer = stream.get();
    //  MinorVer
    minorVer = stream.get();
    //  BuildNumHi
    buildNumHi = stream.get();
    //  BuildNumLow
    buildNumLow = stream.get();

    assert tokenLength + 2 == 11 + vldsReader.getBytesRead();

    return tokenLength + 2;
  }

  /**
   * Token as a {@code String}.
   *
   * @return token fields as a {@code String}.
   */
  @Override
  public String toString() {
    return "LoginAckTokenReader{"
            + "\n  tokenLength=" + tokenLength
            + "\n  interfaceType=" + interfaceType
            + "\n  tdsVersion=0x" + Integer.toHexString(tdsVersion) + " (SQL Server " + tdsVersions.get(tdsVersion) + ")"
            + "\n  progName=" + progName
            + "\n  version=" + majorVer + "." + minorVer
            + "\n  build=" + buildNumHi + "." + buildNumLow
            + "\n}\n";
  }
}
